home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Sound Fx
/
Sound Fx.iso
/
Software
/
UNZIPED
/
SBPLY254
/
SOURCE.ZIP
/
_PCPLAY.ASM
< prev
next >
Wrap
Assembly Source File
|
1996-04-26
|
13KB
|
429 lines
_TEXT SEGMENT WORD PUBLIC 'CODE'
_TEXT ENDS
_DATA SEGMENT WORD PUBLIC 'DATA'
_DATA ENDS
CONST SEGMENT WORD PUBLIC 'CONST'
CONST ENDS
_BSS SEGMENT WORD PUBLIC 'BSS'
;GLOBAL UNINITIATED DATA GOES HERE
_BSS ENDS
DGROUP GROUP CONST,_BSS,_DATA
;
ASSUME DS:DGROUP,SS:DGROUP
;EXTRN EXTERNAL SUBROUTINE CALLS GO HERE
;
; _pcplay FAR Procedure for C Written by John A. Ball April 22, 1996
;
;Plays back sound on the regular pc speaker
;
; void pcplay(char *buffer,unsigned number_read,unsigned frequency
; ,unsigned volume);
;
; pcplay(Buffer Pointer,Length,Frequency,Volume);
;
; Buffer Pointer is a Far pointer to the sound buffer (64k max)
; Length is the number of sound samples to play
; Frequency is the playback frequency in hertz
; Volume is currently not supported
;
_TEXT SEGMENT
ASSUME CS:_TEXT
PUBLIC _pcplay
_pcplay PROC FAR
PARMA EQU [BP+6] ;Sound Buffer Pointer
PARMB EQU [BP+10] ;Number of samples to play (Length)
PARMC EQU [BP+12] ;Frequency to playback samples
PARMD EQU [BP+14] ;Volume 0-7
PARMS EQU 10 ;Number of bytes for local storage
BUFFERPNT EQU [BP-4] ;LOCAL VARIABLES GO HERE
SAMPLES EQU [BP-6]
FREQUENCY EQU [BP-8]
VOLUME EQU [BP-10]
PUSH BP ;Save stack Frame
MOV BP,SP
SUB SP,PARMS ;MAKE SPACE FOR LOCAL VARIABLE (INT) ON STACK
PUSH DS ;Save registers
PUSH ES
PUSH SI
PUSH DI
MOV CS:ERROR,0 ;Initialize error
JMP OVERDATA
DDELAY DW 0
TCOUNT DW 0
MCOUNT DW 0
LCOUNT DW 0
PERIOD DW 0
DOUBLER DW 0
ERROR DW 0
TABLE DB 53,53,53,53,53,52,52,52,52,52,52,52,52,52,52,51
DB 51,51,51,51,51,51,51,51,50,50,50,50,50,50,50,50
DB 49,49,49,49,49,49,49,49,49,48,48,48,48,48,48,48
DB 48,47,47,47,47,47,47,47,46,46,46,46,46,46,46,45
DB 45,45,45,45,45,45,44,44,44,44,44,44,43,43,43,43
DB 43,43,42,42,42,42,42,42,41,41,41,41,41,40,40,40
DB 40,40,39,39,39,39,39,38,38,38,38,37,37,37,37,36
DB 36,36,35,35,35,34,34,34,33,33,32,32,31,30,29,27
DB 25,24,23,22,22,21,21,20,20,19,19,19,18,18,18,17
DB 17,17,17,16,16,16,16,15,15,15,15,14,14,14,14,14
DB 13,13,13,13,12,12,12,12,12,12,11,11,11,11,11,10
DB 10,10,10,10,10,9,9,9,9,9,9,9,8,8,8,8
DB 8,8,7,7,7,7,7,7,7,6,6,6,6,6,6,6
DB 6,5,5,5,5,5,5,5,4,4,4,4,4,4,4,4
DB 3,3,3,3,3,3,3,3,3,2,2,2,2,2,2,2
DB 2,2,1,1,1,1,1,1,1,1,1,0,0,0,0,0
OVERDATA:
LES DI,PARMA ;GET SOUND BUFFER POINTER
MOV BUFFERPNT,DI ;AND SAVE
MOV DI,ES
MOV BUFFERPNT+2,DI
MOV BX,PARMB ;Get number of samples
CMP BX,0 ;Check for zero samples
JNE NORESET
MOV CS:DDELAY,0 ;Reset delay if zero samples
JMP FINISHED
NORESET:
MOV SAMPLES,BX ;AND SAVE
MOV CS:DOUBLER,0
MOV BX,PARMC ;Get frequency
CMP BX,0 ;CHECK FOR DIVIDE BY ZERO
JNE OK0
MOV BX,11000 ;use default if zero
OK0:
CMP BX,0FFFFH ;Is frequency > 32,767 hz?
JL OK21
CMP BX,15000 ;Is frequency > 15,000 hz?
JG OK1
MOV CS:DOUBLER,2 ;Indicate frequency to be doubled
SHL BX,1 ;Double frequency for play back
OK1: CMP BX,22000 ;Is frequency 22,000 hz?
JL OK2
OK2: CMP BX,0FFFFH ;Is frequency 44,000 hz?
JG OK3
OK21: MOV CS:DOUBLER,4 ;Indicate frequency to be halved
SHR BX,1 ;Halve frequency for playback
OK3: MOV FREQUENCY,BX ;and save
MOV DX,000FH ;CALCULATE PERIOD 1000000/FREQUENCY
MOV AX,04240H
DIV BX
MOV CS:PERIOD,AX
;
CPUSPEED:
CMP CS:DDELAY,0 ;SKIP IF ALREADY DONE
JE GETSPEED
JMP BEGIN
GETSPEED:
IN AL,61H ;DISCONNECT SPEAKER FROM TIMER CHIP
PUSH AX ;SAVE STATUS BITS
AND AL,11111100B ;AND STOP TIMER
OUT 61H,AL
JMP $+2
JMP $+2
MOV AL,0B4H ;PROGRAM TIMER 2 FOR MODE 2
OUT 43H,AL
JMP $+2
JMP $+2
MOV AL,0 ;SET TIMER COUNT DOWN MAX COUNT
OUT 42H,AL
JMP $+2
JMP $+2
MOV AL,00H
OUT 42H,AL
JMP $+2
JMP $+2
POP AX ;START TIMER
PUSH AX
OR AL,00000001B
OUT 61H,AL
JMP $+2
POP AX ;STOP TIMER
AND AL,11111100B
OUT 61H,AL
JMP $+2
IN AL,42H ;GET TIMER COUNT
MOV BL,AL
JMP $+2
JMP $+2
IN AL,42H
MOV BH,AL ;BX = TIMER COUNT
MOV AX,0 ;GET NUMBER OF COUNTS USED GETTING THE TIME
SUB AX,BX
MOV CS:TCOUNT,AX ;AND SAVE IT
MOV SI,0
MOV DI,0
MOV CX,10 ;TIME for 10 loops
MOV BH,0
MOV AH,0
CLI ;DISABLE INTERRUPTS DURING TEST
IN AL,61H ;START TIMER
PUSH AX
JMP $+2
OR AL,00000001B
OUT 61H,AL
;
;USE ACTUAL LOOP FOR TIMING BUT DISABLE SPEAKER
;
FETCH1:
LDS BX,BUFFERPNT
MOV AL,[BX+SI] ;Get sample
MOV DI,AX
MOV AL,CS:TABLE[DI] ;and convert to 6 bits
OUT 0E0H,AL ;use E0h so it doesn't interfere with
IN AL,0E0H
OR AL,00000000B ;PRETEND TO START CLOCK
OUT 0E0H,AL
PUSH CX ;Save number of samples
MOV CX,1 ;use minimum time
DELAY1:
JMP $+2
LOOP DELAY1
POP CX ;restore sample count
INC SI
IN AL,0E0H
AND AL,1111111B ;PRETEND TO TURN OFF CLOCK
OUT 0E0H,AL
LOOP FETCH1
POP AX ;STOP TIMER
AND AL,11111100B
OUT 61H,AL
STI ;ENABLE INTERUPTS
IN AL,42H ;GET TIMER COUNT
MOV BL,AL
JMP $+2
JMP $+2
IN AL,42H
MOV BH,AL ;BX = TIMER COUNT
MOV AX,0 ;65536 - TIMER COUNT = DURATION COUNT
SUB AX,BX
int 3
nop
SUB AX,CS:TCOUNT ;SUBTRACT COUNTS USED GETTING THE TIME
MOV DX,0
MOV BX,838
MUL BX ;COUNT X 838 / 10000 = DURATION IN uSECs 100L
MOV BX,10000
DIV BX
MOV BX,AX
MOV AX,CS:PERIOD ;FIND OUT HOW MUCH TIME TO WASTE
SUB AL,BL
MOV AH,0
MOV CS:MCOUNT,AX ;SAVE TIME
MOV CX,100 ;TIME for 100 loops
CLI ;DISABLE INTERRUPTS DURING TEST
IN AL,61H ;START TIMER 2 AGAIN
PUSH AX
JMP $+2
OR AL,00000001B
OUT 61H,AL
TIME1: JMP $+2
LOOP TIME1
POP AX
AND AL,11111100B
OUT 61H,AL
IN AL,42H ;GET TIMER COUNT
MOV BL,AL
JMP $+2
JMP $+2
IN AL,42H
MOV BH,AL ;BX = TIMER COUNT
STI
MOV DX,0
MOV AX,0 ;NOTE THAT TIMER RESTARTS AT INIT COUNT
SUB AX,BX ;IN MODE 2
SUB AX,CS:TCOUNT ;SUBTRACT COUNTS USED GETTING THE TIME
MOV DX,0
MOV BX,838
MUL BX ;COUNT X 838 / 1000 = DURATION IN uSECs 100L
MOV BX,1000
DIV BX
MOV CS:LCOUNT,AX ;SAVE TEMPORARY VALUE
MOV DX,0
MOV AX,CS:MCOUNT ;GET TIME TO WASTE
MOV BX,100
MUL BX ;AND MULTIPLY BY 100
MOV DX,0
MOV BX,CS:LCOUNT ;AND DIVIDE BY LCOUNT
CMP BX,0 ;CHECK FOR DIVIDE BY ZERO
JNE OK
JMP GETSPEED ;TRY AGAIN
OK: DIV BX ;TO GET LOOPS REQUIRED
AND AX,07FFH ;LIMIT TO REASONABLE VALUE
MOV CS:DDELAY,AX ;SAVE LOOP COUNTER VALUE
BEGIN:
MOV AL,092H ;COUNTER 2, LSB ONLY, MODE 1, BINARY
OUT 43H,AL
MOV SI,0
MOV DX,0
MOV AH,0
MOV CX,SAMPLES ;GET NUMBER OF SAMPLES TO PLAY
cli
CMP CS:DOUBLER,0 ;ADJUST PLAYBACK?
JE FETCH
JMP BEGIN1
FETCH:
LDS BX,BUFFERPNT
MOV DL,[BX+SI] ;Get sample
MOV DI,DX ;get index to table
MOV AL,CS:TABLE[DI] ;and convert sample to timer pulse durations
OUT 042H,AL
IN AL,061H
OR AL,00000011B ;START CLOCK
OUT 061H,AL
PUSH CX ;Save number of samples
MOV CX,CS:DDELAY ;GET DELAY LOOP COUNTER
DELAY:
JMP $+2
LOOP DELAY
pop cx ;RESTORE SAMPLE COUNT
INC SI
IN AL,061H
AND AL,1111110B ;TURN OFF CLOCK
OUT 061H,AL
LOOP FETCH ;AND GO GET NEXT SAMPLE
JMP FINISHED
BEGIN1:
CMP CS:DOUBLER,4 ;Halve playback?
JE PLAYHALF
FETCH3:
LDS BX,BUFFERPNT
MOV DL,[BX+SI] ;Get sample
MOV DI,DX ;get index to table
MOV AL,CS:TABLE[DI] ;and convert sample to timer pulse durations
OUT 042H,AL
IN AL,061H
OR AL,00000011B ;START CLOCK
OUT 061H,AL
PUSH CX ;Save number of samples
MOV CX,CS:DDELAY ;GET DELAY LOOP COUNTER
DELAY3:
JMP $+2
LOOP DELAY3
NOP
NOP
IN AL,061H
AND AL,1111110B ;TURN OFF CLOCK
OUT 061H,AL
LDS BX,BUFFERPNT
MOV DL,[BX+SI] ;Get sample
MOV DI,DX ;get index to table
MOV AL,CS:TABLE[DI] ;and convert sample to timer pulse durations
OUT 042H,AL ;Output sample twice at double frequency
IN AL,061H
OR AL,00000011B ;START CLOCK
OUT 061H,AL
MOV CX,CS:DDELAY ;GET DELAY LOOP COUNTER
DELAY4:
JMP $+2
LOOP DELAY4
pop cx ;RESTORE SAMPLE COUNT
INC SI
IN AL,061H
AND AL,1111110B ;TURN OFF CLOCK
OUT 061H,AL
LOOP FETCH3 ;AND GO GET NEXT SAMPLE
JMP FINISHED
PLAYHALF:
SHR CX,1 ;Halve the number of samples
FETCH4:
LDS BX,BUFFERPNT
MOV DL,[BX+SI] ;Get sample
MOV DI,DX ;get index to table
MOV AL,CS:TABLE[DI] ;and convert sample to timer pulse durations
OUT 042H,AL
IN AL,061H
OR AL,00000011B ;START CLOCK
OUT 061H,AL
PUSH CX ;Save number of samples
MOV CX,CS:DDELAY ;GET DELAY LOOP COUNTER
DELAY5:
JMP $+2
LOOP DELAY5
pop cx ;RESTORE SAMPLE COUNT
INC SI ;Increment twice to skip sample
INC SI
IN AL,061H
AND AL,1111110B ;TURN OFF CLOCK
OUT 061H,AL
LOOP FETCH4 ;AND GO GET NEXT SAMPLE
FINISHED:
sti
MOV AH,01H ;See if key pressed
INT 16H
JZ ALL_DONE
KEY_PRESS:
MOV AX,0
INT 16H ;Get the pressed key
CMP AL,1BH ;See if ESC key hit?
JNE ALL_DONE
MOV CS:ERROR,-1 ;Halt output if ESC key pressed
ALL_DONE:
MOV AX,CS:ERROR ;Get error
POP DI
POP SI
POP ES ;Restore registers
POP DS
ADD SP,PARMS ;RESTORE SP
POP BP ;RESTORE BP
RET ;RETURN FAR
_pcplay ENDP
_TEXT ENDS
END